﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using PagedList;
using zqdn.Models;

namespace zqdn.Repositories
{
    public abstract class RepositoryBase<T> where T : class
    {

        private const int DefaultPageSize = 15;

        private zqdnContext _dataContext;
        private readonly IDbSet<T> dbset;
        protected RepositoryBase(IDatabaseFactory databaseFactory)
        {
            DatabaseFactory = databaseFactory;
            dbset = DataContext.Set<T>();
        }

        protected IDatabaseFactory DatabaseFactory
        {
            get;
            private set;
        }

        protected zqdnContext DataContext
        {
            get { return _dataContext ?? (_dataContext = DatabaseFactory.GetDbContext()); }
        }
        public virtual void Add(T entity)
        {
            var tt = entity as ITimestamp;
            if (tt != null)
            {
                tt.CreatedTime = DateTime.Now;
                tt.ModifiedTime = DateTime.Now;
            }
            dbset.Add(entity);
        }
        public virtual void Update(T entity)
        {
            var tt = entity as ITimestamp;
            if (tt != null)
                tt.ModifiedTime = DateTime.Now;
            dbset.Attach(entity);
            _dataContext.Entry(entity).State = System.Data.Entity.EntityState.Modified;
        }
        public virtual void Delete(T entity, bool definite = false)
        {
            ISoftDelete deletedEntity = entity as ISoftDelete;
            if (deletedEntity != null && !definite)
            {
                deletedEntity.IsDeleted = false;
                deletedEntity.DeletedTime = DateTime.Now;
                dbset.Attach(entity);
                _dataContext.Entry(entity).State = System.Data.Entity.EntityState.Modified;
            }
            else
                dbset.Remove(entity);
        }

        public virtual void Delete(Expression<Func<T, bool>> where, bool definite = false)
        {
            IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
            foreach (T obj in objects)
                Delete(obj, definite);
        }
        public virtual T GetById(long id)
        {
            return dbset.Find(id);
        }
        public virtual T GetById(string id)
        {
            return dbset.Find(id);
        }

        public virtual T GetByKeys(params object[] keys)
        {
            return dbset.Find(keys);
        }

        public virtual IQueryable<T> GetAll()
        {
            return dbset;
        }

        public virtual IQueryable<T> GetMany(Expression<Func<T, bool>> where)
        {
            return dbset.Where(where);
        }

        public virtual bool Any(Expression<Func<T, bool>> where)
        {
            return dbset.Any(where);
        }

        /// <summary>
        /// Return a paged list of entities
        /// </summary>
        /// <typeparam name="TOrder"></typeparam>
        /// <param name="page">Which page to retrieve</param>
        /// <param name="where">Where clause to apply</param>
        /// <param name="order">Order by to apply</param>
        /// <returns></returns>
        public virtual IPagedList<T> GetPage<TOrder>(Page page, Expression<Func<T, bool>> where, Expression<Func<T, TOrder>> order, bool isOrderDesc = false)
        {
            return dbset.GetPage<T, TOrder>(where, order, page, isOrderDesc);
        }

        public virtual IPagedList<T> GetPage<TOrder>(Expression<Func<T, bool>> where, Expression<Func<T, TOrder>> order, int pageNumber = 1, int pageSize = DefaultPageSize, bool isOrderDesc = false)
        {
            return GetPage<TOrder>(new Page { PageNumber = pageNumber, PageSize = pageSize }, where, order, isOrderDesc);
        }

        public T Get(Expression<Func<T, bool>> where)
        {
            return dbset.Where(where).FirstOrDefault<T>();
        }

        public void ExecuteSqlCommand(string sql, params object[] parameters)
        {
            _dataContext.Database.ExecuteSqlCommand(sql, parameters);

        }
    }
}